//
//  IMManager.m
//  ChangChat
//
//  Created by Cocoa on 2019/8/28.
//  Copyright © 2019 yibai. All rights reserved.
//

#import "IMManager.h"
#import <AFNetworkReachabilityManager.h>
#import <LKDBHelper/LKDBHelper.h>
#import "YBApiManager+Chat.h"
#import "YBApiManager+Global.h"
#import "YBApiManager.h"
#import "CacheHelper.h"
#import "amrFileCodec.h"
#import <AudioToolbox/AudioToolbox.h>
#import <UserNotifications/UserNotifications.h>


#define LocalNotiReqIdentifer    @"LocalNotiReqIdentifer"

@interface IMManager ()<SRWebSocketDelegate>

@property (nonatomic, strong) NSTimer *heartbeatTimer;
@property (nonatomic, strong) NSTimer *connectFailedTimer;
@property (nonatomic, strong) NSMutableArray *sendMsgArray;


@end

@implementation IMManager

+ (instancetype)shareManager {
    static IMManager *_manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _manager = [[IMManager alloc] init];
    });
    return _manager;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.sendMsgArray = [NSMutableArray array];
        self.isNeedPullMsg = YES;
    }
    return self;
}

- (void)addIMDelegate:(id<IMManagerDelegate>)delegate {
    if (self.delegates == nil) {
        _delegates = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsWeakMemory];
    }
//    __weak UIViewController<IMManagerDelegate> *vc = (UIViewController<IMManagerDelegate> *)delegate;
    [self.delegates addPointer:(void *)delegate];
}
- (void)removeIMDelegate:(id<IMManagerDelegate>)delegate {
    
}

- (void)bindUserIdAction {
    if (kUsermanager.isLogin == NO || self.isReady == NO) {
        return;
    }
    
    if (self.bindUidDelegate && [self.bindUidDelegate respondsToSelector:@selector(imManagerNeedBindUserId)]) {
        [self.bindUidDelegate imManagerNeedBindUserId];
    }
}

#pragma mark - actions
- (void)connect {
    self.webSocket.delegate = nil;
    [self.webSocket close];
    _webSocket = nil;
    self.webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:Socket_ip]];

    self.webSocket.delegate = self;
    [self.webSocket open];
}
- (void)reConnect {
    if (self.webSocket.readyState == SR_OPEN) {
        return;
    }
    static int reConnectTime = 0;
    if (reConnectTime > 1024) {
        reConnectTime = 0;
        return;
    }
    
    @weakify(self);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        @strongify(self);
        if (self.webSocket && (self.webSocket.readyState == SR_OPEN || self.webSocket.readyState == SR_CONNECTING)) {
            return ;
        }
        [self connect];
        NSLog(@"连接第%d", reConnectTime);
        if (reConnectTime == 0) {
            reConnectTime = 2;
        } else {
            reConnectTime *= 2;
        }
    });
}
- (void)closeConnect {
    _isBindUserId = NO;
    _client_id = nil;
    _connectStatus = IMConnectStatus_KickedOffline;
    if (self.webSocket) {
        [self.webSocket closeWithCode:6666 reason:@"手动关闭"];
        _webSocket = nil;
    }
    [self endHeartbeatTimer];
    [self endConnectFailedTimer];
}
- (void)clean {
    _isReady = NO;
    _delegates = nil;
    _isNeedPullMsg = YES;
    [self closeConnect];
}

- (void)startHeartbeatTimer {
    if (self.heartbeatTimer) {
        [self endHeartbeatTimer];
    }
    @weakify(self);
//    dispatch_main_async_safe(^{
    dispatch_async(dispatch_get_main_queue(), ^{

        @strongify(self);
        self.heartbeatTimer = [NSTimer scheduledTimerWithTimeInterval:15 repeats:YES block:^(NSTimer * _Nonnull timer) {
            if (self.webSocket.readyState == SR_OPEN) {
                [self.webSocket sendPing:nil];
                NSLog(@"--发送心跳包");
                if (self.isBindUserId == NO) {
                    [self bindUserIdAction];
                }
            }
        }];
        [[NSRunLoop currentRunLoop] addTimer:self.heartbeatTimer forMode:NSRunLoopCommonModes];
    });
}
- (void)endHeartbeatTimer {
//    @weakify(self);
    dispatch_main_async_safe(^{
//        @strongify(self);
        if (self.heartbeatTimer) {
            [self.heartbeatTimer invalidate];
            self.heartbeatTimer = nil;
        }
    });
}
- (void)startConnectFailedTimer {
    if (self.connectFailedTimer) {
        [self endConnectFailedTimer];
    }
    @weakify(self);
    dispatch_async(dispatch_get_main_queue(), ^{
        self.connectFailedTimer = [NSTimer scheduledTimerWithTimeInterval:10 repeats:YES block:^(NSTimer * _Nonnull timer) {
            @strongify(self);
            if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus != AFNetworkReachabilityStatusNotReachable)
            {
                //关闭网络检测定时器
                [self endConnectFailedTimer];
                //开始重连
                [self reConnect];
                NSLog(@"--连接失败后重连");
            }
        }];
        [[NSRunLoop currentRunLoop] addTimer:self.connectFailedTimer forMode:NSRunLoopCommonModes];
    });
}
- (void)endConnectFailedTimer {
    @weakify(self);
    dispatch_main_async_safe(^{
        @strongify(self);
        if (self.connectFailedTimer) {
            [self.connectFailedTimer invalidate];
            self.connectFailedTimer = nil;
        }
    });
}



#pragma mark - srWebSocket delegate
//  连接成功 启动心跳
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    
    NSLog(@"\n%s [Line %d]", __PRETTY_FUNCTION__, __LINE__);
    _connectStatus = IMConnectStatus_Connected;
    for (id<IMManagerDelegate> delegate in self.delegates) {
        if ([delegate respondsToSelector:@selector(imManager:connectStatusChanged:)] == YES) {
            [delegate imManager:self connectStatusChanged:self.connectStatus];
        }
    }
    [self startHeartbeatTimer];
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
    NSLog(@"\n%s [Line %d]", __PRETTY_FUNCTION__, __LINE__);
    _isBindUserId = NO;
    _connectStatus = IMConnectStatus_Failed;
    for (id<IMManagerDelegate> delegate in self.delegates) {
        if ([delegate respondsToSelector:@selector(imManager:connectStatusChanged:)] == YES) {
            [delegate imManager:self connectStatusChanged:self.connectStatus];
        }
    }
    if ([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
        [self startConnectFailedTimer];
    } else {
        [self reConnect];
    }
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
    NSLog(@"\n%s [Line %d], reason = %@, code = %ld", __PRETTY_FUNCTION__, __LINE__, reason, code);
    _isBindUserId = NO;
    if (code == 6666) {
        _connectStatus = IMConnectStatus_KickedOffline;
    } else {
        _connectStatus = -1;
        if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
            [self startConnectFailedTimer];//开启网络检测
        } else {
            NSLog(@"关闭连接");
            [self closeConnect];
            [self reConnect];//连接失败就重连
        }
    }
    for (id<IMManagerDelegate> delegate in self.delegates) {
        if ([delegate respondsToSelector:@selector(imManager:connectStatusChanged:)] == YES) {
            [delegate imManager:self connectStatusChanged:self.connectStatus];
        }
    }
    [self endHeartbeatTimer];
}
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload {
//    NSLog(@"\n%s [Line %d]", __PRETTY_FUNCTION__, __LINE__);
}
///  收到消息
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
    NSLog(@"\n%s [Line %d], --msg = %@", __PRETTY_FUNCTION__, __LINE__, message);
    NSDictionary *msgDic = [message mj_JSONObject];
    if ([msgDic[@"type"] isHaveStr] == NO) {
        return;
    }
    NSInteger type = [msgDic[@"type"] integerValue];
    if (type == -1) {
        _client_id = msgDic[@"client_id"];
        [self bindUserIdAction];
    }
    else if (type == -2) {
        [[NSNotificationCenter defaultCenter] postNotificationName:notification_TokenFailed object:nil];
        [MBProgressHUD showTipMessageInWindow:@"您的登录已失效"];
    }
    else if (type == MsgType_User_update || type == MsgType_User_nickname_update) {
        NSDictionary *friendInfoDic = [msgDic objectForKey:@"content"];
        FriendItem *friend = [FriendItem mj_objectWithKeyValues:friendInfoDic];
        
        FriendItem *oldF = [FriendItem searchSingleWithWhere:@{@"friend_id": friend.friend_id} orderBy:nil];
        if (type == MsgType_User_update) {
            friend.nickname = oldF.nickname;
        } else {
            oldF.nickname = friend.nickname;
            friend = oldF;
        }
        if (friend.isMe) {
            [kUsermanager saveUserWithInfo:friendInfoDic];
        }
        [friend updateToDB];
        [[NSNotificationCenter defaultCenter] postNotificationName:notification_Friend_Info_Changed object:friend];
        
    }
    else if (type == MsgType_Friend_apply || type == MsgType_Friend_agree || type == MsgType_Friend_delete || type == MsgType_Friend_rejected) {// 收到申请加我好友请求 // 收到同意加我好友消息 // 收到被删除好友消息
        MsgItem *msg = [MsgItem mj_objectWithKeyValues:message];
        for (id<IMManagerDelegate> delegate in self.delegates) {
            if ([delegate respondsToSelector:@selector(imManager:receiveFriendStatusMsg:)]) {
                [delegate imManager:self receiveFriendStatusMsg:msg];
            }
        }
        
        NSString *tip = nil;
        if (type == MsgType_Friend_apply) {
            tip = @"收到一条好友申请";
        } else if (type == MsgType_Friend_agree) {
            tip = @"您的好友申请已通过";
        } else if (type == MsgType_Friend_rejected) {
            tip = @"您的好友请求已被拒绝";
        }
        [self sendLocalNotificationWithTitle:@"畅聊" subtitle:@"收到一条新消息" body:tip];
        AudioServicesPlaySystemSound(1007);

        
        //  标记已收到
        MsgModel *model = MsgModel.new;
        model.msg = msg;
        [self receiveMsgWithMsgModel:model];
    }
    else if (type == MsgType_Msg_recall) {
        NSString *msgid = msgDic[@"message_id"];
        MsgItem *msg = [MsgItem searchSingleWithWhere:@{@"message_id": msgid} orderBy:nil];
        if (msg == nil) {
            msg = [MsgItem searchSingleWithWhere:@{@"remote_msg_id": msgid} orderBy:nil];
            if (msg == nil) {
                msg = [MsgItem mj_objectWithKeyValues:msgDic];
                msg.type = MsgType_Tip;
                [MsgItem insertToDB:msg];
            }
        }
        msg.type = MsgType_Tip;
        msg.content = msgDic[@"content"];
            [msg updateToDB];
            for (id<IMManagerDelegate> delegate in self.delegates) {
                if ([delegate respondsToSelector:@selector(imManager:recallMsg:)]) {
                    [delegate imManager:self recallMsg:msg];
                }
            }
//        }];
        //  标记已收到
        MsgModel *model = MsgModel.new;
        model.msg = msg;
        [self receiveMsgWithMsgModel:model];
    }
    else if(type == MsgType_Msg_read) {
        MsgItem *msg = [MsgItem mj_objectWithKeyValues:msgDic];
        for (id<IMManagerDelegate> delegate in self.delegates) {
            if ([delegate respondsToSelector:@selector(imManager:readMsg:)]) {
                [delegate imManager:self readMsg:msg];
            }
        }
    }
    else if (type == MsgType_Group_update) {
        [kUsermanager getGroupInfoWithGroupId:msgDic[@"group_id"] needUpdate:YES callbackBlock:^(GroupItem * _Nonnull item) {
            [[NSNotificationCenter defaultCenter] postNotificationName:notification_GroupChatInfo_updated object:item];
        }];
    }
    else if ([msgDic[@"group_id"] intValue] > 0){
        
        [self receiveMsgWithMsgDic:msgDic];
    }
}

#pragma mark - receive msg socket
- (void)receiveMsgWithMsgDic:(NSDictionary *)msgDic {
//    MsgType type = [msgDic[@"type"] integerValue];

    //  封装消息体
    MsgModel *model = [MsgModel new];
    MsgItem *msg = [MsgItem mj_objectWithKeyValues:msgDic];
    msg.sendStatus = SendStatus_receiving;
    model.msg = msg;
    //  插入数据库
    if ([MsgItem searchWithWhere:@{@"message_id": @(msg.message_id)}].count > 0){
        return;
    }
    if (msg.message_id != 0) {
        [MsgItem insertToDB:msg];
    }
    //  分发代理
    for (id<IMManagerDelegate> delegate in self.delegates) {
        if ([delegate respondsToSelector:@selector(imManager:receiveMsg:)] == YES) {
            [delegate imManager:self receiveMsg:model];
        }
    }
    //  标记已收到
    if (msg.message_id != 0) {
        [self receiveMsgWithMsgModel:model];
    }
    //  如果是音频文件， 预下载音频
    if (msg.type == MsgType_Voice) {
        [self downloadVoiceWithMsgModel:model];
    }
    
    //  声音、震动、本地通知 提示
    if (msg.type == MsgType_Time || msg.type == MsgType_Tip) {
        return;
    }

    [kUsermanager getGroupWithGroupId:msg.group_id callbackBlock:^(GroupItem * _Nonnull item) {
        if (item.no_disturb == YES) {
            return;
        }
        [self sendLocalNotificationWithTitle:@"畅聊" subtitle:item.name body:msg.lastMsgTip];
        AudioServicesPlaySystemSound(1007);
        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
    }];
}
#pragma mark - send msg  (http)
- (void)sendMsg:(MsgModel *)msgModel {
    //  插入数据库
    msgModel.msg.sendStatus = SendStatus_failed;
    [self insertMsgToDB:msgModel];
    
    [self resendMsg:msgModel];
}
- (void)resendMsg:(MsgModel *)msgModel {
    msgModel.msg.sendStatus = SendStatus_sending;
    [msgModel.msg updateToDB];
    if (msgModel.msg.type == MsgType_Text || msgModel.msg.type == MsgType_Card) {
        //  发送http请求
        [self sendHttpRequestWithMsgModel:msgModel];
    } else if (msgModel.msg.type == MsgType_Photo ||
               msgModel.msg.type == MsgType_Voice ||
               msgModel.msg.type == MsgType_Video ||
               msgModel.msg.type == MsgType_File) {
        [self uploadFileWithMsgModel:msgModel];
    }
}
- (void)uploadFileWithMsgModel:(MsgModel *)msgModel {
    
    if (msgModel.msg.content.isHaveStr == YES) {
        [self sendHttpRequestWithMsgModel:msgModel];
        return;
    }
    
    @weakify(self);
    [[YBApiManager.shareManager uploadFileWithFilePath:msgModel.msg.filePath type:msgModel.msg.type] subscribeNext:^(id  _Nullable x) {
        NSString *fileUrl = [[x objectForKey:@"data"] objectForKey:@"url"];
        msgModel.msg.content = fileUrl;
        @strongify(self);
        [self sendHttpRequestWithMsgModel:msgModel];
    } error:^(NSError * _Nullable error) {
        msgModel.msg.sendStatus = SendStatus_failed;
        [msgModel.msg updateToDB];
        @strongify(self);
        for (id<IMManagerDelegate> delegate in self.delegates) {
            if ([delegate respondsToSelector:@selector(imManager:sendMsgFailed:)] == YES) {
                [delegate imManager:self sendMsgFailed:msgModel];
            }
        }
    }];
}
- (void)downloadVoiceWithMsgModel:(MsgModel *)msgModel {
    [[YBApiManager.shareManager downloadTask:msgModel.msg.content] subscribeNext:^(id  _Nullable x) {
        if ([x isKindOfClass:NSNumber.class] == YES) {
            return;
        }
        NSURL *fileURL = x;
        NSLog(@"download file path = %@", fileURL);
        NSData *data = [NSData dataWithContentsOfURL:fileURL];
        data = DecodeAMRToWAVE(data);
        [[CacheHelper helper] cacheFileWitType:MsgType_Voice fileData:data completion:^(NSString * _Nonnull filePath) {
            msgModel.msg.filePath = filePath;
            [msgModel.msg updateToDB];
            [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
        }];
        
    } error:^(NSError * _Nullable error) {
        NSLog(@"download error = %@", error);
    }];
}
- (void)sendHttpRequestWithMsgModel:(MsgModel *)msgModel {
    MsgItem *item = msgModel.msg;
    @weakify(self);
    [[YBApiManager.shareManager sendMsgWithContent:item.content client_id:self.client_id group_id:item.group_id type:[NSString kk_stringWithInteger:item.type] s_image:[item valueForKey:@"s_image"] second:item.second file_name:item.file_name file_size:item.file_size] subscribeNext:^(id  _Nullable x) {
        
        @strongify(self);
        msgModel.msg.sendStatus = SendStatus_success;
        msgModel.msg.remote_msg_id = [[x objectForKey:@"data"] integerValue];
        [msgModel.msg updateToDB];
        for (id<IMManagerDelegate> delegate in self.delegates) {
            if ([delegate respondsToSelector:@selector(imManager:sendMsgSuccess:)] == YES) {
                [delegate imManager:self sendMsgSuccess:msgModel];
            }
        }
        
    } error:^(NSError * _Nullable error) {
        msgModel.msg.sendStatus = SendStatus_failed;
        [msgModel.msg updateToDB];
        @strongify(self);
        for (id<IMManagerDelegate> delegate in self.delegates) {
            if ([delegate respondsToSelector:@selector(imManager:sendMsgFailed:)] == YES) {
                [delegate imManager:self sendMsgFailed:msgModel];
            }
        }
    }];
}
- (void)receiveMsgWithMsgModel:(MsgModel *)model {
    if (model.msg.sendStatus == SendStatus_success ) {
        return;
    }
    [[YBApiManager.shareManager sendReceivedMsgWithMsgId:[NSString kk_stringWithInteger:model.msg.message_id]] subscribeNext:^(id  _Nullable x) {
        if (model.msg.type == MsgType_Friend_agree ||
            model.msg.type == MsgType_Friend_apply ||
            model.msg.type == MsgType_Friend_delete) {
            return ;
        }
        model.msg.sendStatus = SendStatus_success;
        [model.msg updateToDB];
    }];
}

- (void)insertMsgToDB:(MsgModel *)msgModel {
    [MsgItem insertToDB:msgModel.msg];
}
- (NSInteger)getSendMessageId {
    NSInteger msgId = [[NSUserDefaults standardUserDefaults] integerForKey:@"last_SendMessageId"];
    msgId--;
    [[NSUserDefaults standardUserDefaults] setInteger:msgId forKey:@"last_SendMessageId"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    return msgId;
}

#pragma mark - 触发本地通知
- (void)sendLocalNotificationWithTitle:(NSString *)title subtitle:(NSString *)subtitle body:(NSString *)body {
    
//    NSString *title = title;
//    NSString *sutitle = subtitle;
//    NSString *body = body;
//    NSInteger badge = 1;
//    NSInteger timeInteval = 5;
    NSDictionary *userInfo = @{@"id":@"LOCAL_NOTIFY_SCHEDULE_ID"};
    
    
        // 1.创建通知内容
        UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
//        [content setValue:@(YES) forKeyPath:@"shouldAlwaysAlertWhileAppIsForeground"];
        content.sound = [UNNotificationSound defaultSound];
//        content.title = title;
        content.subtitle = subtitle;
        content.body = body;
//        content.badge = @(badge);
    
        content.userInfo = userInfo;
        
//        // 2.设置通知附件内容
//        NSError *error = nil;
//        NSString *path = [[NSBundle mainBundle] pathForResource:@"logo_img_02@2x" ofType:@"png"];
//        UNNotificationAttachment *att = [UNNotificationAttachment attachmentWithIdentifier:@"att1" URL:[NSURL fileURLWithPath:path] options:nil error:&error];
//        if (error) {
//            NSLog(@"attachment error %@", error);
//        }
//        content.attachments = @[att];
//        content.launchImageName = @"icon_certification_status1@2x";
        // 2.设置声音
//        UNNotificationSound *sound = [UNNotificationSound soundNamed:@"sound01.wav"];// [UNNotificationSound defaultSound];
//        sound = [UNNotificationSound defaultSound];
//        content.sound = sound;
    
        // 3.触发模式
//        UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0 repeats:NO];
    
        // 4.设置UNNotificationRequest
        UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:LocalNotiReqIdentifer content:content trigger:nil];
        
        // 5.把通知加到UNUserNotificationCenter, 到指定触发点会被触发
        [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
            
        }];
    
}
@end
